home *** CD-ROM | disk | FTP | other *** search
/ Risc World 3 / Risc World 3.iso / SOFTWARE / ISSUE6 / PD / PDF / pdf / c++ / Printer < prev    next >
Text File  |  2003-02-14  |  19KB  |  652 lines

  1. //--------------------------------------------------------------------------
  2. //
  3. //   Copyright (c) 2002, Colin Granville
  4. //
  5. //   All rights reserved.
  6. //
  7. //   Redistribution and use in source and binary forms, with or
  8. //   without modification, are permitted provided that the following 
  9. //   conditions are met:
  10. //
  11. //      * Redistributions of source code must retain the above copyright 
  12. //        notice, this list of conditions and the following disclaimer.
  13. //
  14. //      * Redistributions in binary form must reproduce the above 
  15. //        copyright notice, this list of conditions and the following 
  16. //        disclaimer in the documentation and/or other materials 
  17. //        provided with the distribution.
  18. //
  19. //      * The name Colin Granville may not be used to endorse or promote 
  20. //        products derived from this software without specific prior 
  21. //        written permission.
  22. //
  23. //   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
  24. //   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
  25. //   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
  26. //   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
  27. //   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
  28. //   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
  29. //   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
  30. //   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
  31. //   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
  32. //   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  33. //   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
  34. //   OF THE POSSIBILITY OF SUCH DAMAGE.
  35. //
  36. //--------------------------------------------------------------------------
  37.  
  38. #include "Printer.h"
  39. #include "Document.h"
  40. #include "GuiTargets.h"
  41. #include "GuiWimpMessage.h"
  42. #include "GuiWindow.h"
  43. #include "GuiGadget.h"
  44. #include "GuiTask.h"
  45. #include "guilib:gfx.h"
  46. #include "guilib:ERROR.h"
  47. #include "DrawOutputDevice.h"
  48. #include "iostream.h"
  49. #include "GuiHourGlass.h"
  50. #include "UserEvents.h"
  51.  
  52. class PrintJob
  53.   public:
  54.     PrintJob(const char* filename,const char* title=0);
  55.     ~PrintJob();
  56.     void abort();
  57.     operator void*()    {return job?this:0;}
  58.     int operator!()     {return !job;}
  59.  
  60.   private:
  61.     void close()        {if (job) {_swix(OS_Find,_INR(0,1),0,job);job=0;}}
  62.     int job;
  63.     int oldJob;
  64. };
  65.  
  66. //*************************************************************************
  67.  
  68. PrintJob::PrintJob(const char* filename,const char* title)
  69.   : job(0),
  70.     oldJob(0)
  71. {
  72.   if (_swix(OS_Find,_INR(0,1) | _OUT(0),0x8f,filename,&job) == 0 &&
  73.       _swix(PDriver_SelectJob,_INR(0,1) | _OUT(0),job,title,&oldJob)==0) return;
  74.  
  75.   close();
  76. }
  77.  
  78. //*************************************************************************
  79.  
  80. PrintJob::~PrintJob()
  81. {
  82.   if (!job) return;
  83.   _swix(PDriver_EndJob,_IN(0),job);
  84.   _swix(PDriver_SelectJob,_INR(0,1),oldJob,0);
  85.   close();
  86. }
  87.  
  88. //*************************************************************************
  89.  
  90. void PrintJob::abort()
  91. {
  92.   if (!job) return;
  93.   _swix(PDriver_AbortJob,_IN(0),job);
  94.   _swix(PDriver_SelectJob,_INR(0,1),oldJob,0);
  95.   close();
  96. }
  97.  
  98. //*************************************************************************
  99. //*************************************************************************
  100. //*************************************************************************
  101.  
  102. class PrintArea
  103. {
  104.   public:
  105.     enum {FULL_PAGE,TOP_HALF,BOTTOM_HALF};
  106.     PrintArea(Document& document,int page,int page_type,bool side2,const DocViewChoices&,bool isLandscape);
  107.     _kernel_oserror* render(int id,GuiBBox& bounds);
  108.  
  109.   private:
  110.     int id;
  111.     DrawOutputDevice  outDev;
  112.     GuiTransform      trfm;
  113.     bool              isBlank;
  114. };
  115.  
  116.  
  117. //*************************************************************************
  118.  
  119. int nextId()
  120. {
  121.   static int n=1;
  122.   n^=1;
  123.   return n;
  124. }
  125.  
  126. PrintArea::PrintArea(Document& document,int page,int page_type, bool side2,
  127.                      const DocViewChoices& viewChoices,bool isLandscape)
  128.  : id(nextId()),
  129.    isBlank(page<=0)
  130. {
  131.   
  132.   if (page<0) return;
  133.   int paper_width,paper_height;
  134.   if (_swix(PDriver_PageSize,_OUTR(1,2),&paper_width,&paper_height)) return;
  135.  
  136.   if (page_type != FULL_PAGE) paper_height/=2;
  137.   int y_offset=(page_type==TOP_HALF ? paper_height : 0);
  138.  
  139.   GuiBBox bounds;
  140.   if (page > 0)
  141.   {
  142.     outDev.setMoreColours(gTrue);
  143.     outDev.setNoImages(viewChoices.getNoImages());
  144.     outDev.setNoText(viewChoices.getNoText());
  145.     outDev.setNoImages(viewChoices.getNoDrawings());
  146.     outDev.setNoText(viewChoices.getNoType3Fonts());
  147.  
  148.     int rotation=0;
  149.     if (page_type != FULL_PAGE ||
  150.         (isLandscape && page_type == FULL_PAGE) ) rotation=(side2 ? 90 : 270);
  151.  
  152.     document.getPage(page,&outDev,DrawOutputDevice::DPI,rotation);
  153.  
  154.     trfm.m0 = (1<<16);//1.08b9 (page_type == FULL_PAGE ? (1<<16) : 46341);// (1/root2)
  155.                          trfm.m1 = 0;
  156.     trfm.m2 = 0;         trfm.m3 = trfm.m0;
  157.     trfm.m4 = 0;         trfm.m5 = 0;
  158.  
  159.     outDev.getDrawFile().getBBox(bounds,&trfm);
  160.     int wid=bounds.getWidth()*25/16;  //convert to millipoints
  161.     int ht =bounds.getHeight()*25/16;
  162.     
  163.     if (wid>paper_width || ht>paper_height)
  164.     {
  165.       double scale = (double)paper_width/(double)wid;
  166.       double yscale= (double)paper_height/(double)ht;
  167.       if (scale>yscale) scale=yscale;
  168.  
  169.       trfm.m0 = (int)((double)trfm.m0*scale);
  170.       trfm.m3 = (int)((double)trfm.m3*scale);
  171.  
  172.       wid=(int)((double)wid*scale);
  173.       ht=(int)((double)ht*scale);
  174.     }
  175.     trfm.m4=((paper_width-wid)/2)*16/25;
  176.     trfm.m5=((paper_height-ht)/2+y_offset)*16/25;
  177.   }
  178.   
  179.   bounds(0,y_offset/400,paper_width/400,(paper_height+y_offset)/400);
  180.  
  181.   int printer_trfm[4];
  182.   printer_trfm[0]=(1<<16); printer_trfm[1]=0;
  183.   printer_trfm[2]=0;       printer_trfm[3]=printer_trfm[0];
  184.  
  185.   int origin[2];
  186.   origin[0]=0;
  187.   origin[1]=y_offset;
  188.   _swix(PDriver_GiveRectangle,_INR(0,4),id,&bounds,printer_trfm,origin,0xFFFFFF00);
  189. }
  190.  
  191. //*************************************************************************
  192.  
  193. _kernel_oserror* PrintArea::render(int id_, GuiBBox& bounds)
  194. {
  195.   if (id!=id_ || isBlank) return 0;
  196.   return outDev.getDrawFile().render(&trfm,&bounds,0);
  197. }
  198.  
  199. //*************************************************************************
  200. //*************************************************************************
  201. //*************************************************************************
  202.  
  203. class PrinterProxy : public Node
  204. {
  205.   public:
  206.     virtual ~PrinterProxy();
  207.     DECLARE_RTTI();
  208. };
  209.  
  210.  
  211. //*************************************************************************
  212. //*************************************************************************
  213. //*************************************************************************
  214.  
  215. PrintData::PrintData()
  216.  : document(0),
  217.    copies(1),
  218.    flags(0),
  219.    from(1),
  220.    to(1),
  221.    start(0),
  222.    end(0),
  223.    sectionSize(0)
  224. {
  225. }
  226.  
  227. //*************************************************************************
  228. //*************************************************************************
  229. //*************************************************************************
  230.  
  231. class Printer
  232. {
  233.   public:
  234.     friend Printer& printer();
  235.     bool print(const PrintData&);
  236.     void release()     {claimed=0;} //called by PrinterProxy when it is deleted
  237.   private:
  238.     Node*              claimed;
  239.     int                myRef;
  240.     GuiWindow          mainWin;
  241.     GuiActionButton    continueButton;
  242.     GuiActionButton    cancelButton;
  243.     GuiButton          statusText;
  244.     PrintData          data;
  245.  
  246.  
  247.     Printer();
  248.     ~Printer();
  249.     
  250.     void finished();
  251.     _kernel_oserror* printOneSide();
  252.     _kernel_oserror* printSide(int page,int bottom_page,bool turn_clockwise);
  253.     void startPrinting();
  254.  
  255.     GUI_DECLARE_EVENT_TARGETS(Printer);
  256.  
  257.     GuiToolboxTarget printSide2Target;
  258.     Claim printSide2(GuiToolboxEvent&,const GuiIdBlock&);
  259.  
  260.     GuiWimpTarget    protocolBounced;
  261.     Claim noPrinter(GuiWimpPollBlock&, const GuiIdBlock&);
  262.  
  263.     GuiMessageTarget protocolA;
  264.     Claim printFile(GuiWimpMessage&);
  265.     Claim printTypeOdd(GuiWimpMessage&);
  266.  
  267.     GuiMessageTarget protocolB;
  268.     Claim printError(GuiWimpMessage&);
  269.     Claim dataSaveAck(GuiWimpMessage&);
  270.  
  271.     GuiNullTarget nullEvent;
  272.     void startProtocol();
  273.     
  274. };
  275.  
  276. //*************************************************************************
  277.  
  278. Printer::Printer()
  279.  : claimed(0),
  280.    mainWin("printStatus"),
  281.    continueButton(mainWin,0),
  282.    cancelButton(mainWin,2),
  283.    statusText(mainWin,1),
  284.    printSide2Target(&mainWin,User_PrintSide2,this,Printer::printSide2)
  285. {
  286. }
  287.  
  288. //*************************************************************************
  289.  
  290. Printer::~Printer() {}
  291.  
  292. //*************************************************************************
  293.  
  294. inline void set_short(char*& buf,int i)
  295. {
  296.   *buf++=(i & 0xff);
  297.   *buf++=((i>>8) & 0xff);
  298. }
  299.  
  300. static void set_mouse_rectangle(const GuiBBox* box=0)
  301. {
  302.   char buf[16];
  303.   char*b=buf;
  304.   *b++=1;
  305.   set_short(b,(box ? box->xmin : 0));
  306.   set_short(b,(box ? box->ymin : 0));
  307.   set_short(b,(box ? box->xmax : gfx::screenwidth()-1));
  308.   set_short(b,(box ? box->ymax : gfx::screenheight()-1));
  309.   _swix(OS_Word,_INR(0,1),21,buf);
  310. }
  311.  
  312. //*************************************************************************
  313.  
  314. void Printer::finished()
  315. {
  316.   protocolBounced.destroy();
  317.   protocolA.destroy();
  318.   protocolB.destroy();
  319.   delete claimed;
  320.   claimed=0;
  321.   mainWin.hide();
  322. }
  323.  
  324. //*************************************************************************
  325.  
  326. bool Printer::print(const PrintData& d)
  327. {
  328.   if (claimed)  return 0;
  329.   if (!d.document) return 0;
  330.   data=d;
  331.   claimed=data.document->addChild(new PrinterProxy); //keeps document open if no views
  332.   if (!claimed) return 0;
  333.  
  334.   mainWin.showCentred();
  335.   startPrinting();
  336.   return 1;
  337. }
  338. //*************************************************************************
  339.  
  340. void Printer::startPrinting()
  341. {
  342.   statusText.setValue(guiTask().lookup("PRINT_ABORT:Press <Escape> to Abort"));
  343.   cancelButton.fade(1);
  344.   continueButton.fade(1);
  345.   nullEvent(this,Printer::startProtocol);
  346. }
  347.  
  348. //*************************************************************************
  349.  
  350. void Printer::startProtocol()
  351. {
  352.   nullEvent.destroy();
  353.  
  354.   protocolBounced(0,GuiWimp_EUserMessageAcknowledge,this,Printer::noPrinter);
  355.   protocolA(GuiWimp_MPrintFile,this,Printer::printFile);
  356.   protocolB(GuiWimp_MPrintError,this,Printer::printError);
  357.  
  358.   GuiWimpMessage mess;
  359.   mess.hdr.size=44;
  360.   myRef=mess.sendRecorded(GuiWimp_MPrintSave);
  361. }
  362.  
  363. //*************************************************************************
  364.  
  365. Claim Printer::noPrinter(GuiWimpPollBlock& wpb,const GuiIdBlock&)
  366. {
  367.   GuiWimpMessage& mess= (GuiWimpMessage&) wpb;
  368.   if (mess.hdr.myRef!=myRef) return DONT_CLAIM;
  369.   finished();
  370.   return CLAIM;
  371. }
  372.  
  373. //*************************************************************************
  374.  
  375. Claim Printer::printError(GuiWimpMessage& mess)
  376. {
  377.   if (mess.hdr.yourRef!=myRef) return DONT_CLAIM;
  378.   finished();
  379.   if (mess.hdr.size==20)
  380.      WARN("Printer Busy");
  381.   else
  382.      WARN((const _kernel_oserror*)&mess.data);
  383.   return CLAIM;
  384. }
  385.  
  386. //*************************************************************************
  387.  
  388. Claim Printer::printFile(GuiWimpMessage& mess)
  389. {
  390.   if (mess.hdr.yourRef!=myRef) return DONT_CLAIM;
  391.  
  392.   protocolBounced.destroy();
  393.   protocolA(GuiWimp_MPrintTypeOdd,this,Printer::printTypeOdd);
  394.   protocolB(GuiWimp_MDataSaveAck,this,Printer::dataSaveAck);
  395.   return CLAIM;
  396. }
  397.  
  398. //*************************************************************************
  399.  
  400. Claim Printer::printTypeOdd(GuiWimpMessage& mess)
  401. {
  402.   if (mess.hdr.yourRef!=myRef) return DONT_CLAIM;
  403.   protocolA.destroy();
  404.   protocolB.destroy();
  405.   mess.reply(GuiWimp_MPrintTypeKnown);
  406.  
  407.   GuiHourglass hourglass;
  408.   _kernel_oserror* err =printOneSide();
  409.   if (err ||
  410.      (data.flags & PrintData::IS_DOUBLE_SIDED)==0 ||
  411.      data.flags & PrintData::IS_SIDE2)
  412.   {
  413.     finished();
  414.     WARN(err);
  415.     return CLAIM;
  416.   }
  417.  
  418.   GuiGetWindowStateBlock state;
  419.   mainWin.getState(state);
  420.   set_mouse_rectangle(&state.visibleArea);
  421.  
  422.   statusText.setValue(guiTask().lookup("PRINT_SIDE2:Turn paper over"));
  423.   cancelButton.fade(0);
  424.   continueButton.fade(0);
  425.   gfx::vdu(7);
  426.   return CLAIM;
  427. }
  428.  
  429. //*************************************************************************
  430.  
  431. Claim Printer::printSide2(GuiToolboxEvent&,const GuiIdBlock& id_block)
  432. {
  433.   set_mouse_rectangle();
  434.   if (id_block.self.component!=continueButton.componentId())
  435.   {
  436.     finished();
  437.     return CLAIM;
  438.   }
  439.   data.flags |= PrintData::IS_SIDE2;
  440.   startPrinting();
  441.   return CLAIM;
  442. }
  443. //*************************************************************************
  444.  
  445. Claim Printer::dataSaveAck(GuiWimpMessage&)
  446. {
  447.   finished();
  448.   WARN("Printer Busy");
  449.   return CLAIM;
  450. }
  451.  
  452. //*************************************************************************
  453.  
  454. _kernel_oserror* Printer::printOneSide()
  455. {
  456.   PrintJob job("printer:","PDF");
  457.   if (!job) return _kernel_last_oserror();
  458.  
  459.   if (_swix(PDriver_CheckFeatures,_INR(0,1),1<<29,1<<29)==0)
  460.   {
  461.     DrawOutputFontList::declareFonts(); 
  462.     _swix(PDriver_DeclareFont,_INR(0,2),0,0,0);
  463.   }
  464.  
  465.   int copies=data.copies;
  466.   if (copies<1 || (data.flags & PrintData::IS_COLLATE)==0) copies=1;
  467.   for (;copies;copies--)
  468.   {
  469.     int from = data.from;
  470.     if (from==0) from=1;
  471.  
  472.     int to = data.to;
  473.     if (to==0) to = data.document->getPageCount();
  474.  
  475.     if (data.flags & PrintData::IS_ALL)
  476.     {
  477.       from=1;
  478.       to=data.document->getPageCount();
  479.     }
  480.     if (to<from) to=from;
  481.  
  482.     int number_of_sheets=to-from+1;
  483.     int step=(data.flags & PrintData::IS_REVERSES_SHEETS?-1:1);
  484.     int page=(data.flags & PrintData::IS_REVERSES_SHEETS?to:from);
  485.     int fold_total=0;
  486.     int turn_clockwise=0;
  487.  
  488.     if (data.flags & PrintData::IS_DOUBLE_SIDED)
  489.     {
  490.       int section_start=1;
  491.       int section_end=data.document->getPageCount();
  492.       if (data.flags & PrintData::IS_START_END)
  493.       {
  494.         if (data.start>section_start) section_start=data.start;
  495.         if (data.end && data.end<section_end) section_end=data.end;
  496.       }
  497.       if (section_end<section_start) section_end=section_start;
  498.       if (from<section_start) from=section_start;
  499.       if (to>section_end) to=section_end;
  500.       if (to<from) to=from;
  501.  
  502.       if (data.flags & PrintData::IS_PAMPHLET)
  503.       { 
  504.         fold_total=((section_end-section_start)|3)+section_start*2;
  505.         int first_page=(from-section_start)&~1;
  506.         int last_page=(to-section_start)|1;
  507.  
  508.         int section_size =((section_end-section_start)|3);
  509.  
  510.         if (first_page>(section_size/2))
  511.         {
  512.           int n=section_size-first_page;
  513.           first_page=section_size-last_page;
  514.           last_page=n;
  515.         }
  516.         else if (last_page>(section_size/2))
  517.         {
  518.           last_page=section_size-last_page;
  519.           if (last_page<first_page) first_page=last_page;
  520.           last_page=section_size/2;
  521.         }
  522.  
  523.         first_page&=~1;
  524.         last_page&=~1;
  525.         number_of_sheets=(last_page-first_page)/2 + 1;
  526.  
  527.         from=first_page+section_start;
  528.         to=fold_total-from;
  529.         if (to>section_end) to=section_end;
  530.  
  531.         page=last_page+section_start;
  532.         step=-2;
  533.       }
  534.       else
  535.       {
  536.         from = ((from-section_start)&~1)+section_start;
  537.         to   = ((to-section_start)|1)+section_start;
  538.         number_of_sheets = (to-from+1)/2;
  539.         if (to>section_end) to=section_end;
  540.  
  541.         page=((to-section_start) &~1)+section_start;
  542.         step=-2;
  543.       }
  544.  
  545.       if (data.flags & PrintData::IS_SIDE2)
  546.       {
  547.         if ((data.flags & PrintData::IS_REVERSES_SHEETS)==0) {page+=1;turn_clockwise=1;}
  548.       }
  549.       else
  550.       {
  551.         if (data.flags & PrintData::IS_TURNS_PAPER_OVER) {page=from;step=2;}
  552.         if (data.flags & PrintData::IS_REVERSES_SHEETS)  {page+=1;turn_clockwise=1;}
  553.       }
  554.     }
  555.  
  556.     int i=0;
  557.     _kernel_oserror* err=0;
  558.     for (;i<number_of_sheets;i++) 
  559.     {
  560.       int bottom_page=-1; //-1 no bottom_page, 0 blank bottom page;
  561.       if (fold_total) bottom_page=fold_total-page;
  562.       err=printSide((page>=from && page<=to ? page : 0),
  563.                     (bottom_page==-1 || (bottom_page>=from && bottom_page<=to) ? bottom_page : 0),
  564.                     turn_clockwise);
  565.  
  566.       if (err)
  567.       {
  568.         job.abort();
  569.         return err;
  570.       }
  571.  
  572.       GuiHourglass::percentage((i+1)*100/number_of_sheets);
  573. //cerr << (page>=from && page<=to ? page : 0) << ' '
  574. //     << (bottom_page==-1 || (bottom_page>=from && bottom_page<=to) ? bottom_page : 0) << ' '
  575. //     << turn_clockwise
  576. //     << endl;
  577.       page+=step;
  578.     }
  579.   }
  580.   return 0; 
  581. }
  582.  
  583. //*************************************************************************
  584.  
  585. _kernel_oserror* Printer::printSide(int top_page,int bottom_page,bool turn_clockwise)
  586. {
  587.   PrintArea top(*data.document,top_page,
  588.                 (bottom_page<0 ? PrintArea::FULL_PAGE : PrintArea::TOP_HALF),
  589.                 turn_clockwise,data.viewChoices,data.flags & PrintData::IS_LANDSCAPE);
  590.   PrintArea bottom(*data.document,bottom_page,PrintArea::BOTTOM_HALF,turn_clockwise,data.viewChoices,
  591.                    data.flags & PrintData::IS_LANDSCAPE);
  592.   GuiBBox bounds;
  593.   int more=0;
  594.   int id;
  595.   _kernel_oserror* err=0;
  596.   int force_page=1;
  597.   int copies=data.copies;
  598.   if (copies <= 0 || data.flags & PrintData::IS_COLLATE) copies=1;
  599.   for (err=_swix(PDriver_DrawPage,_INR(0,3)|_OUT(0)|_OUT(2),copies,&bounds,0,0,&more,&id);
  600.        !err && more;
  601.        err=_swix(PDriver_GetRectangle,_IN(1) |_OUT(0)|_OUT(2),&bounds,&more,&id))
  602.   {
  603.       if (force_page)
  604.       {
  605.         // force blank page by making sure something is printed on it
  606.         // can't use 0xffffff00 as colour as it still thinks nothing is printed
  607.         // so put the smallest of dots on the page
  608.         static int path[]={2,0,0,8,0,1*256,0,0};
  609.         static int trfm[]={(1<<16),0,0,(1<<16),0,0};
  610.         gfx::gcol_bgr(0,0x0);
  611.         trfm[4] = (bounds.xmin+8)*256-1;
  612.         trfm[5] = (bounds.ymin+8)*256-1;
  613.         err=_swix(Draw_Stroke,_INR(0,6),path,0,trfm,0,0,0,0);
  614.         force_page=0;
  615.       }
  616.  
  617.       err=top.render(id,bounds);
  618.       if (!err) err=bottom.render(id,bounds);
  619.   }
  620.   return err;
  621. }
  622.  
  623. //*************************************************************************
  624. //*************************************************************************
  625. //*************************************************************************
  626.  
  627. PrinterProxy::~PrinterProxy()
  628. {
  629.   printer().release();
  630. }
  631.  
  632. DEFINE_RTTI_DERIVED(PrinterProxy,Node);
  633.  
  634. //*************************************************************************
  635. //*************************************************************************
  636. //*************************************************************************
  637.  
  638. Printer& printer()
  639. {
  640.     static Printer p;
  641.     return p;
  642. }
  643.  
  644. //*************************************************************************
  645.  
  646. bool Printer_print(const PrintData& data)
  647. {
  648.   return printer().print(data);
  649. }
  650.  
  651.